/*
 * QE3e Qt Quick 3D Engine.
 * Copyright (C) 2010 Joris Vaillant and Mathieu Pujol 
 * <joris.vaillant@gmail.com>  <mathieu.pujol87@gmail.com>
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */


#ifndef QE3E_INTERVAL_H
#define QE3E_INTERVAL_H

// global
#include "qe3e_global.h"

// includes
// QE3e
#include "Util.h"
// Qt4
#include <QtGlobal>
// Std
#include <limits>
#include <cmath>

namespace qe3e
{

  class QE3E_EXPORT Interval
  {
  public:
    static const Interval True;
    static const Interval False;
    static const Interval Indefinite;
    static const Interval Full;
    static const Interval Empty;
  public:
    Interval();
    Interval(double inf, double sup);

    double inf() const;
    void setInf(double inf);

    double sup() const;
    void setSup(double sup);

    bool operator==(const Interval& interval) const;
    bool operator!=(const Interval& interval) const;

    Interval operator &&(const Interval & interval);
    Interval operator ||(const Interval & interval);

    Interval operator+(const Interval& interval) const;
    Interval operator-(const Interval& interval) const;
    Interval operator*(const Interval& interval) const;
    Interval operator/(const Interval& interval) const;

    Interval operator+(float scal) const;
    Interval operator-(float scal) const;
    Interval operator*(float scal) const;

    Interval sqrt() const;

    template<int P>
    Interval pow() const;

    Interval pow2() const;
    Interval cube() const;
    Interval pow4() const;

    double length() const;

    bool include(const Interval &interval) const;
    bool include(double pts) const;

    Interval isPositive() const;

  private:
    template <int P, bool IsPair>
    struct calc_pow
    {
    };

    template <int P>
    struct calc_pow<P, true>
    {
      static Interval pow(const Interval& i);
    };

    template <int P>
    struct calc_pow<P, false>
    {
      static Interval pow(const Interval& i);
    };

  private:
    double m_inf;
    double m_sup;
  };


  // inline functions
  inline Interval::Interval()
    : m_inf(-std::numeric_limits<double>::max())
    , m_sup(std::numeric_limits<double>::max())
  {
  }

  inline Interval::Interval(double inf, double sup)
  {
    if (inf <= sup)
    {
      m_inf = inf;
      m_sup = sup;
    }
    else
    {
      m_inf = sup;
      m_sup = inf;
    }
  }

  inline double Interval::inf() const
  {
    return m_inf;
  }

  inline void Interval::setInf(double inf)
  {
    m_inf = inf;
  }

  inline double Interval::sup() const
  {
    return m_sup;
  }

  inline void Interval::setSup(double sup)
  {
    m_sup = sup;
  }

  inline bool Interval::operator==(const Interval& interval) const
  {
    return m_inf == interval.m_inf && m_sup == interval.m_sup;
  }

  inline bool Interval::operator!=(const Interval& interval) const
  {
    return m_inf != interval.m_inf || m_sup != interval.m_sup;
  }

  inline Interval Interval::operator+(const Interval& interval) const
  {
    return Interval(m_inf + interval.m_inf, m_sup + interval.m_sup);
  }

  inline Interval Interval::operator-(const Interval& interval) const
  {
    return Interval(m_inf - interval.m_sup, m_sup - interval.m_inf);
  }

  inline Interval Interval::operator*(const Interval& i) const
  {
    return Interval(qMin(m_inf*i.m_inf,
                         qMin(m_inf*i.m_sup,
                              qMin(m_sup*i.m_inf, m_sup*i.m_sup)))
                    , qMax(m_inf*i.m_inf,
                           qMax(m_inf*i.m_sup,
                                qMax(m_sup*i.m_inf, m_sup*i.m_sup))));
  }

  inline Interval Interval::operator/(const Interval& i) const
  {
    if(i.m_inf == 0.f || i.m_sup == 0.f)
      return Indefinite;

    return Interval(qMin(m_inf/i.m_inf,
                         qMin(m_inf/i.m_sup,
                              qMin(m_sup/i.m_inf, m_sup/i.m_sup)))
                    , qMax(m_inf/i.m_inf,
                           qMax(m_inf/i.m_sup,
                                qMax(m_sup/i.m_inf, m_sup/i.m_sup))));
  }

  inline Interval Interval::operator+(float scal) const
  {
    return Interval(m_inf + scal, m_sup + scal);
  }

  inline Interval Interval::operator-(float scal) const
  {
    return Interval(m_inf - scal, m_sup - scal);
  }

  inline Interval Interval::operator*(float scal) const
  {
    if(scal >= 0.f)
      return Interval(m_inf*scal, m_sup*scal);
    else
      return Interval(m_sup*scal, m_inf*scal);
  }

  inline Interval Interval::operator&&(const Interval & interval)
  {
    return Interval(m_inf && interval.m_inf, m_sup && interval.m_sup);
  }

  inline Interval Interval::operator||(const Interval & interval)
  {
    return Interval(m_inf || interval.m_inf, m_sup || interval.m_sup);
  }

  inline Interval Interval::sqrt() const
  {
    if(m_inf >= 0.)
    {
      return Interval(::sqrt(m_inf), ::sqrt(m_sup));
    }

    return False;
  }

  inline Interval Interval::pow2() const
  {
    return pow<2>();
  }

  inline Interval Interval::cube() const
  {
    return pow<3>();
  }

  inline Interval Interval::pow4() const
  {
    return pow<4>();
  }

  inline double Interval::length() const
  {
    return m_sup - m_inf;
  }

  inline bool Interval::include(const Interval &interval) const
  {
    if(m_inf >= interval.m_inf && m_inf <= interval.m_sup)
      return true;
    if(m_sup <= interval.m_sup && m_sup >= interval.m_inf)
      return true;
    return false;
  }

  inline bool Interval::include(double pts) const
  {
    return pts >= m_inf && pts <= m_sup;
  }

  inline Interval Interval::isPositive() const
  {
    if(m_inf > 0 && m_sup > 0)
      return True;
    else if(m_inf < 0 && m_sup < 0)
      return False;
    else
      return Indefinite;
  }

  template <int P>
  inline Interval Interval::pow() const
  {
    return calc_pow<P, TestPair<P>::isPair>::pow(*this);
  }

  template <int P>
  inline Interval Interval::calc_pow<P, false>::pow(const Interval& i)
  {
    return Interval(qe3e::pow<P>(i.m_inf), qe3e::pow<P>(i.m_sup));
  }

  template <int P>
  inline Interval Interval::calc_pow<P, true>::pow(const Interval& i)
  {
    Interval ret = i.isPositive();
    if(ret == Indefinite)
    {
      return Interval(0., qMax(qe3e::pow<P>(i.m_inf), qe3e::pow<P>(i.m_sup)));
    }
    else if(ret == True)
    {
      return Interval(qe3e::pow<P>(i.m_inf), qe3e::pow<P>(i.m_sup));
    }
    else
    {
      return Interval(qe3e::pow<P>(i.m_sup), qe3e::pow<P>(i.m_inf));
    }
  }

}
#endif // QE3E_INTERVAL_H
